//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using OpenLS.UI;

namespace OpenLS.Spreadsheet.UI.Internals
{
    public class FocusElement: DependencyObject
    {

        public static ICommand GetGotFocusCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(GotFocusCommandProperty);
        }

        public static void SetGotFocusCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(GotFocusCommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GotFocusCommandProperty =
            DependencyProperty.RegisterAttached("GotFocusCommand", typeof(ICommand), typeof(FocusElement), new UIPropertyMetadata(null, OnGotFocusCommandChanged));

        private static void OnGotFocusCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement target = (UIElement) d;
            ICommand oldValue = (ICommand) e.OldValue;
            ICommand newValue = (ICommand) e.NewValue;
            if (oldValue != null)
                target.GotFocus -= new RoutedEventHandler(target_GotFocus);
            if (newValue != null)
                target.GotFocus += new RoutedEventHandler(target_GotFocus);
        }

        static void target_GotFocus(object sender, RoutedEventArgs e)
        {
            ICommand command = GetGotFocusCommand((DependencyObject) sender);
            if (command is RoutedCommand)
            {
                ((RoutedCommand)command).Execute(null, (IInputElement) sender);
            }
            else
                command.Execute(null);
        }


        public static bool GetAutoFocus(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoFocusProperty);
        }

        public static void SetAutoFocus(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoFocusProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoFocus.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoFocusProperty =
            DependencyProperty.RegisterAttached("AutoFocus", typeof(bool), typeof(FocusElement), new UIPropertyMetadata(false, OnAutoFocusChanged));

        private static void OnAutoFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)d;
            if (element.IsLoaded)
                element.Focus();
            else
            {
                element.Loaded += delegate { element.Focus(); };
            }
        }


        public static IInputElement GetFocusRoot(DependencyObject obj)
        {
            return (IInputElement)obj.GetValue(FocusRootProperty);
        }

        public static void SetFocusRoot(DependencyObject obj, IInputElement value)
        {
            obj.SetValue(FocusRootProperty, value);
        }

        // Using a DependencyProperty as the backing store for FocusRoot.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FocusRootProperty =
            DependencyProperty.RegisterAttached("FocusRoot", typeof(IInputElement), typeof(FocusElement), new FrameworkPropertyMetadata(null,FrameworkPropertyMetadataOptions.Inherits));



        public static IInputElement GetNextFocusedElement(DependencyObject obj)
        {
            return (IInputElement)obj.GetValue(NextFocusedElementProperty);
        }

        public static void SetNextFocusedElement(DependencyObject obj, IInputElement value)
        {
            obj.SetValue(NextFocusedElementProperty, value);
        }

        // Using a DependencyProperty as the backing store for NextFocusedElement.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NextFocusedElementProperty =
            DependencyProperty.RegisterAttached("NextFocusedElement", typeof(IInputElement), typeof(FocusElement), new FrameworkPropertyMetadata(null,
                FrameworkPropertyMetadataOptions.Inherits));





        public static IInputElement GetFocusRedirect(DependencyObject obj)
        {
            return (IInputElement)obj.GetValue(FocusRedirectProperty);
        }

        public static void SetFocusRedirect(DependencyObject obj, IInputElement value)
        {
            obj.SetValue(FocusRedirectProperty, value);
        }

        // Using a DependencyProperty as the backing store for FocusRedirect.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FocusRedirectProperty =
            DependencyProperty.RegisterAttached("FocusRedirect", typeof(IInputElement), typeof(FocusElement), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnFocusRedirectChanged)));

        private static void OnFocusRedirectChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            IInputElement oldValue = (IInputElement) e.OldValue;
            IInputElement newValue = (IInputElement) e.NewValue;
            if (oldValue == newValue)
            {
                return;
            }
            FrameworkElement t = (FrameworkElement) sender;
            if (newValue != null)
            {
                t.GotFocus += new RoutedEventHandler(t_GotFocus);
            }
            else
            {
                t.GotFocus -= t_GotFocus;
            }
        }

        static void t_GotFocus(object sender, RoutedEventArgs e)
        {
            FrameworkElement newFocus = (FrameworkElement) GetFocusRedirect((DependencyObject)sender);
            if (newFocus == sender)
            {
                e.Handled = true;
                return;
            }
            if (e.OriginalSource is MenuItem)
            {
                return;
            }
            if (Enumerable.Any(WindowServices.GetAncestors((Visual)e.Source), v => v == newFocus))
                return;
            if (newFocus == null)
                return;
            while ((GetFocusRoot(newFocus) != null && GetFocusRoot(newFocus) != newFocus) ||
                (VisualTreeHelper.GetChildrenCount(newFocus)==1 && ((newFocus is ContentPresenter) || GetFocusDown(newFocus))))
            {
                if (newFocus is ContentPresenter)
                {
                    // Contract.Assert(VisualTreeHelper.GetChildrenCount(newFocus) == 1);
                    bool childHasFocus = (VisualTreeHelper.GetChildrenCount(newFocus) == 1);
                    if (!childHasFocus) return;
                    newFocus = (FrameworkElement) VisualTreeHelper.GetChild(newFocus, 0);
                }
                else if (GetFocusDown( newFocus) && VisualTreeHelper.GetChildrenCount(newFocus) == 1)
                {
                    // Contract.Assert(VisualTreeHelper.GetChildrenCount(newFocus) == 1);
                    bool childHasFocus = (VisualTreeHelper.GetChildrenCount(newFocus) == 1);
                    if (!childHasFocus) return;
                    newFocus = (FrameworkElement)VisualTreeHelper.GetChild(newFocus, 0);
                    
                }
                else
                    newFocus = (FrameworkElement) GetFocusRoot(newFocus);
                
            }
            newFocus.Focus();
        }




        public static bool GetFocusDown(DependencyObject obj)
        {
            return (bool)obj.GetValue(FocusDownProperty);
        }

        public static void SetFocusDown(DependencyObject obj, bool value)
        {
            obj.SetValue(FocusDownProperty, value);
        }

        // Using a DependencyProperty as the backing store for FocusDown.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FocusDownProperty =
            DependencyProperty.RegisterAttached("FocusDown", typeof(bool), typeof(FocusElement), new UIPropertyMetadata(false));

        


    }
}
